﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.Reports
{
	/// <summary>
	/// 
	/// </summary>
	[Serializable]
	public class SetManipulationInstruction:InstructionBase
	{
		#region props
		private Dictionary<string,SetDataProvider> _Providers;
		/// <summary></summary>
		public Dictionary<string,SetDataProvider> Providers
		{
			get
			{
				return this._Providers;
			}
			set
			{
				this._Providers = value;
			}
		}

		private List<SetJoin> _Joins;
		/// <summary></summary>
		public List<SetJoin> Joins
		{
			get
			{
				return this._Joins;
			}
			set
			{
				this._Joins = value;
			}
		}

		private List<SetOperation> _Operations;
		/// <summary></summary>
		public List<SetOperation> Operations
		{
			get
			{
				return this._Operations;
			}
			set
			{
				this._Operations = value;
			}
		}

		private List<string> _ExportedDataSetNames;
		/// <summary></summary>
		public List<string> ExportedDataSetNames
		{
			get
			{
				return this._ExportedDataSetNames;
			}
			set
			{
				this._ExportedDataSetNames = value;
			}
		}

		private string _ResultFilePath;
		/// <summary>result was saved as xml file</summary>
		public string ResultFilePath
		{
			get
			{
				return this._ResultFilePath;
			}
			set
			{
				this._ResultFilePath = value;
			}
		}
		#endregion

		#region ctor
		/// <summary>
		/// 
		/// </summary>
		public SetManipulationInstruction():base(Guid.Empty,Guid.Empty)
		{
			this._Providers=new Dictionary<string, SetDataProvider>();
			this._Joins=new List<SetJoin>();
			this._Operations=new List<SetOperation>();
			this._ExportedDataSetNames=new List<string>();
			this._ResultFilePath = string.Empty;
		}

		/// <summary>
		/// constructor, all derived class must call default constructor to initialize
		/// default settings.
		/// </summary>
		public SetManipulationInstruction(Guid componentID, Guid jobTicketID,
			Dictionary<string,SetDataProvider> providers,
			List<SetJoin> joins,
			List<SetOperation> operations,
			List<string> exportedSetNames,
			string resultFilePath) 
			: base(componentID, jobTicketID)
		{
			this._Providers = providers;
			this._Joins = joins;
			this._Operations = operations;
			this._ExportedDataSetNames = exportedSetNames;
			this._ResultFilePath = resultFilePath;
		}
		#endregion

		#region Overrides of InstructionBase

		/// <summary>
		/// serialize settings to xml
		/// the function below is mainly for storing into database as serialized form instead of string
		/// </summary>
		public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
		{
			XmlNode xNode = XmlDataUtil.AddElement(ref xDoc, parentNode, "Instruction");
			XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Type", this.GetType().FullName);
			XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ComponentID", this.OwnerComponentID.ToString());
			XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "WorkerID", this.OwnerWorkerInstanceID.ToString());
			XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SubmitTime", this.SubmitTime.ToString());
			XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "StartTime", this.StartTime.ToString());

			XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ResultFilePath",this.ResultFilePath);

			if(this.Providers !=null && this.Providers.Count>0)
			{
				XmlNode providerRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Providers");
				foreach(string setName in this.Providers.Keys)
				{
					SetDataProvider provider = this.Providers[setName];
					XmlNode providerNode = XmlDataUtil.AddElement(ref xDoc, providerRootNode, "Provider");
					XmlDataUtil.UpdateAttribute(ref xDoc, providerNode,"SetName",setName);
					XmlDataUtil.UpdateAttribute(ref xDoc, providerNode,"SourceType",provider.SourceType.ToString());
					XmlDataUtil.UpdateAttribute(ref xDoc, providerNode,"DataSourcePath",provider.DataSourcePath);
					XmlDataUtil.UpdateAttribute(ref xDoc, providerNode,"RecordListPath",provider.RecordListPath);
					XmlDataUtil.UpdateAttribute(ref xDoc, providerNode,"KeyFieldName",provider.KeyFieldName);
					if(provider.FieldPaths !=null && provider.FieldPaths.Count>0)
					{
						XmlNode fieldRootNode = XmlDataUtil.AddElement(ref xDoc, providerNode, "Fields");
						foreach(string fieldLabel in provider.FieldPaths.Keys)
						{
							XmlNode fieldNode = XmlDataUtil.AddElement(ref xDoc, fieldRootNode, "Field");
							XmlDataUtil.UpdateAttribute(ref xDoc, fieldNode, "FieldName",fieldLabel);
							XmlDataUtil.UpdateAttribute(ref xDoc, fieldNode, "FieldPath",provider.FieldPaths[fieldLabel]);
						}
					}
				}
			}

			if(this.Joins !=null && this.Joins.Count>0)
			{
				XmlNode joinRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Joins");
				foreach(SetJoin join in this.Joins)
				{
					XmlNode joinNode = XmlDataUtil.AddElement(ref xDoc, joinRootNode, "Join");
					XmlDataUtil.UpdateAttribute(ref xDoc, joinNode, "LeftSetName",join.LeftSetName);
					XmlDataUtil.UpdateAttribute(ref xDoc, joinNode, "LeftKeyNames",StringUtil.ToString(join.LeftKeyNames));
					XmlDataUtil.UpdateAttribute(ref xDoc, joinNode, "LeftTransformation",join.LeftKeyTransformation);
					XmlDataUtil.UpdateAttribute(ref xDoc, joinNode, "RightSetName",join.RightSetName);
					XmlDataUtil.UpdateAttribute(ref xDoc, joinNode, "RightKeyNames",StringUtil.ToString(join.RightKeyNames));
					XmlDataUtil.UpdateAttribute(ref xDoc, joinNode,"RightTransformation",join.RightKeyTransformation);
				}
			}

			if(this.Operations !=null && this.Operations.Count>0)
			{
                this.Operations.Sort();
			    int opOrder = 0;
                for(int i=0;i<this.Operations.Count;i++)
                {
                    opOrder++;
                    this.Operations[i].Order = opOrder;
                }

				XmlNode opRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Operations");
				foreach(SetOperation op in this.Operations)
				{
					XmlNode opNode = XmlDataUtil.AddElement(ref xDoc, opRootNode, "Operation");
                    XmlDataUtil.UpdateAttribute(ref xDoc, opNode, "Order",op.Order.ToString());
					XmlDataUtil.UpdateAttribute(ref xDoc, opNode, "SetNames",StringUtil.ToString(op.SetNames));
					XmlDataUtil.UpdateAttribute(ref xDoc, opNode,"Formular",op.Formular);
					XmlDataUtil.UpdateAttribute(ref xDoc, opNode,"OutputSetName",op.OutputSetName);
				}
			}

			if(this.ExportedDataSetNames !=null && this.ExportedDataSetNames.Count>0)
			{
				XmlNode setRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "ExportSets");
				foreach(string exportSetName in this.ExportedDataSetNames)
				{
					XmlNode setNode = XmlDataUtil.AddElement(ref xDoc, setRootNode, "Set");
					XmlDataUtil.UpdateAttribute(ref xDoc, setNode,"SetName",exportSetName);
				}
			}

			return xNode;
		}

		/// <summary>
		/// instantiated from xml
		/// "&lt;Instructions&gt;&lt;Instruction&gt;&lt;/Instruction&gt;&lt;/Instruction&gt;", must
		/// instantiate from first child of root node
		/// </summary>
		public override InstructionBase Instantiate(XmlNode xNode)
		{
			if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
			{
				SetManipulationInstruction instruction = new SetManipulationInstruction();
				instruction.OwnerComponentID = new Guid(xNode.Attributes.GetNamedItem("ComponentID").Value);
				instruction.OwnerWorkerInstanceID = new Guid(xNode.Attributes.GetNamedItem("WorkerID").Value);
				instruction.SubmitTime = DateTime.Parse(xNode.Attributes.GetNamedItem("SubmitTime").Value);
				instruction.StartTime = DateTime.Parse(xNode.Attributes.GetNamedItem("StartTime").Value);

				instruction.ResultFilePath = XmlDataUtil.GetAttributeValue(xNode, "ResultFilePath", "");

				XmlNodeList providerNodes = xNode.SelectNodes("Providers/Provider");
				if (providerNodes != null && providerNodes.Count > 0)
				{
					instruction.Providers = new Dictionary<string, SetDataProvider>();
					foreach (XmlNode providerNode in providerNodes)
					{
						SetDataProvider provider = new SetDataProvider();
						provider.SetName = XmlDataUtil.GetAttributeValue(providerNode, "SetName", "");
						provider.SourceType =
							(SetDataSourceType) Enum.Parse(
							                    	typeof (SetDataSourceType),
							                    	XmlDataUtil.GetAttributeValue(
							                    		providerNode, "SourceType",
							                    		SetDataSourceType.XML.ToString()));
						provider.DataSourcePath = XmlDataUtil.GetAttributeValue(providerNode, "DataSourcePath", "");
						provider.RecordListPath = XmlDataUtil.GetAttributeValue(providerNode, "RecordListPath", "");
						provider.KeyFieldName = XmlDataUtil.GetAttributeValue(providerNode, "KeyFieldName", "");
						XmlNodeList fieldNodes = providerNode.SelectNodes("Fields/Field");
						if (fieldNodes != null && fieldNodes.Count > 0)
						{
							provider.FieldPaths = new Dictionary<string, string>();
							foreach (XmlNode fieldNode in fieldNodes)
							{
								string label = XmlDataUtil.GetAttributeValue(fieldNode, "FieldName", "");
								string path = XmlDataUtil.GetAttributeValue(fieldNode, "FieldPath", "");
								provider.FieldPaths.Add(label, path);
							}
						}
						instruction.Providers.Add(provider.SetName, provider);
					}
				}

				XmlNodeList joinNodes = xNode.SelectNodes("Joins/Join");
				if(joinNodes !=null && joinNodes.Count>0)
				{
					instruction.Joins=new List<SetJoin>();
					foreach(XmlNode joinNode in joinNodes)
					{
						string leftSetName = XmlDataUtil.GetAttributeValue(joinNode, "LeftSetName", "");
						string[] leftKeyNames = StringUtil.Split(XmlDataUtil.GetAttributeValue(joinNode, "LeftKeyNames", ""));
						string leftTransformation = XmlDataUtil.GetAttributeValue(joinNode, "LeftTransformation", "");
						string rightSetName = XmlDataUtil.GetAttributeValue(joinNode, "RightSetName", "");
						string[] rightKeyNames = StringUtil.Split(XmlDataUtil.GetAttributeValue(joinNode, "RightKeyNames", ""));
						string rightTransformation = XmlDataUtil.GetAttributeValue(joinNode, "RightTransformation", "");
						SetJoin join = new SetJoin(leftSetName, leftKeyNames, leftTransformation, rightSetName, rightKeyNames,
						                           rightTransformation);
						instruction.Joins.Add(join);
					}
				}

				XmlNodeList opNodes = xNode.SelectNodes("Operations/Operation");
				if(opNodes !=null && opNodes.Count>0)
				{
					instruction.Operations=new List<SetOperation>();
					foreach(XmlNode opNode in opNodes)
					{
					    int order = int.Parse(XmlDataUtil.GetAttributeValue(opNode, "Order", "0"));
						string[] setNames = StringUtil.Split(XmlDataUtil.GetAttributeValue(opNode, "SetNames", ""));
						string formular = XmlDataUtil.GetAttributeValue(opNode, "Formular", "");
						string outputSetName = XmlDataUtil.GetAttributeValue(opNode, "OutputSetName", "");
						SetOperation op=new SetOperation(order, setNames,formular, outputSetName);
						instruction.Operations.Add(op);
					}
                    instruction.Operations.Sort();
				    int opOrder = 0;
                    for(int i=0;i<instruction.Operations.Count;i++)
                    {
                        opOrder++;
                        instruction.Operations[i].Order = opOrder;
                    }
				}

				instruction.ExportedDataSetNames = new List<string>();
				XmlNodeList setNodes = xNode.SelectNodes("ExportSets/Set");
				if(setNodes !=null && setNodes.Count>0)
				{
					foreach(XmlNode setNode in setNodes)
					{
						instruction.ExportedDataSetNames.Add(XmlDataUtil.GetAttributeValue(setNode, "SetName", ""));
					}
				}
				
				return instruction;
			}
			else
			{
				throw new Exception("Unable to instantiate instruction: invalid type specified in xml node");
			}
		}

		#endregion

        public override Dictionary<string, Type> GetBindableMemberNames(Type[] excludeMemberFromTypes)
        {
            Dictionary<string,Type> bindings = base.GetBindableMemberNames(excludeMemberFromTypes);
            if(this.Providers !=null && this.Providers.Count>0)
            {
                foreach(string inputName in this.Providers.Keys)
                {
                    if (!bindings.ContainsKey(inputName))
                        bindings.Add(inputName, typeof (string));
                }
            }
            return bindings;
        }

        public override object ReadMemberValue(string memberName)
        {
            if(this.Providers !=null && this.Providers.ContainsKey(memberName))
            {
                SetDataProvider provider = this.Providers[memberName];
                return provider.SetName;
            }
            return base.ReadMemberValue(memberName);
        }

        public override void SetMemberValue(string memberName, object memberValue)
        {
            if (this.Providers != null && this.Providers.ContainsKey(memberName))
            {
                SetDataProvider provider = this.Providers[memberName];
                provider.DataSourcePath = (string)memberValue;
            }
            else
                base.SetMemberValue(memberName, memberValue);
        }
	}
}
